/*
 * video-graphics.c
 *
 *  Created on: 6/Mar/2010
 *      Author: Ângela Igreja
 */

#include "video-graphics.h"
#include "sprite.h"
#include "title.h"

#ifndef NULL
#define NULL (void *)0
#endif

int HRES = 0;
int VRES = 0;

ulong get_memory_video_address(int mode)
{
	__dpmi_regs regs;
	unsigned long dosbuf, address=0;
	dosbuf = __tb & 0xFFFFF; /* dosbuf � a �rea de transfer�ncia */
	dosmemput(&address, 4, dosbuf+40); /*apaga bytes 40 a 43 dessa �rea*/
	regs.x.ax = 0x4F01; /* VBE get mode info */
	regs.x.di = dosbuf & 0xF; /* LSB da �rea de transfer�ncia */
	regs.x.es = (dosbuf >> 4) & 0xFFFF; /*MSB da �rea de transfer�ncia*/
	regs.x.cx = mode; /*informa��o sobre o modo*/
	__dpmi_int(0x10, &regs); /* DOS int 10h */

	if (regs.h.ah)
	{
		return 0;
    } /* tratamento de erro, modo n�o suportado */

	dosmemget(dosbuf+40, 4, &address); /* l� o endere�o da mem�ria v�deo*/

	if (!address)
	{
		return 0;
    } /* tratamento de erro, modo n�o suportado */

	return address;
}


char * enter_graphics(int mode, __dpmi_meminfo *map)
{
	//Gera interrupcao de software 0x10 com os registos indicados, activando o modo grafico
	__dpmi_regs regs;
	regs.x.ax = 0x4F02; //AX especifica a rotina de mudanca do modo da placa
	regs.x.bx = 0x4000 + mode; //BX especifica o modo grafico, com o bit 14 a 1
	__dpmi_int(0x10, &regs); //interrupcao - entrar no modo grafico
	__djgpp_nearptr_enable(); //permite acesso a memoria fisica usando apontadores

	map->address = get_memory_video_address(mode); //obter endereco da memoria video

	//Inicializar/actualizar variaveis HRES e VRES
	if(mode == 0x101)
	{
		HRES = 640;
		VRES = 480;
	}
	else
		if(mode == 0x103)
		{
			HRES = 800;
			VRES = 600;
		}
		else
			if(mode == 0x105)
			{
			  HRES = 1024;
			  VRES = 768;
			}
			else
				if(mode == 0x107)
				{
					HRES = 1280;
					VRES = 1024;
				}
	map->size = HRES * VRES; //tamanho do bloco de memoria (cada pixel ocupa um byte na memoria)
	__dpmi_physical_address_mapping(map); //mapear bloco de memoria pretendido

	return (char*)(map->address + __djgpp_conventional_base); //retornar endereco generico a usar
}


void leave_graphics(__dpmi_meminfo map)
{
	__dpmi_free_physical_address_mapping(&map); /* liberta mapeamento */
	__djgpp_nearptr_disable();

	__dpmi_regs regs;
	regs.x.ax = 0x0003; /* registo AX do CPU com valor 0x03 */
	__dpmi_int(0x10, &regs); /* gera interrup��o software 0x10, entrando no modo texto */
}


void set_pixel(int x, int y, int color, char *base)
{
	if ( x >= HRES || y >= VRES || x < 0 || y <0 ) return;
	*(base + x + y*HRES) = color;
}


int get_pixel(int x, int y, char *base)
{
	if ( x >= HRES || y >= VRES || x < 0 || y <0 ) return 0;
    return  * ( base + x + y * HRES);
}


void clear_screen(char color, char *base)
{
	int x, y;

	for( y = 0 ; y < VRES ;y++ )
	{
		for( x = 0 ; x < HRES ;x++ )
		{
			set_pixel (x, y, color, base);
		}
	}
}


int abs( int num )
{
	if ( num < 0 ) return -1 *num;
	return num;
}


void draw_line(int xi, int yi, int xf, int yf, int color, char *base)
{
	float declive = 0, ordenada_origem = 0;
	int y = 0, x = 0;
	set_pixel( xf, yf, color,base);
	set_pixel( xi, yi, color,base);
	if ( abs( xf-xi ) >= abs( yf-yi ) )
	{
		if ( xf < xi )
		{
			x = xi;
			y = yi;
			xi = xf;
			yi = yf;
			xf = x;
			yf = y;
			x = 0;
			y = 0;
		}
		declive = (float) (yf-yi) /(float)  (xf-xi);
		ordenada_origem = (float) yi - declive* xi;
		for (x=xi ; x<xf; x++)
		{
          y = declive * x + ordenada_origem;
          set_pixel( x, y, color,base);
		}
	}
	else
	{
		if ( yf < yi )
		{
			x = xi;
			y = yi;
			xi = xf;
			yi = yf;
			xf = x;
			yf = y;
			x = 0;
			y = 0;
		}
		declive =(float)  (xf-xi) / (float) (yf-yi);
		ordenada_origem = (float)xi - declive* yi;
		for (y=yi ; y<yf; y++)
		{
          x = declive * y + ordenada_origem;
          set_pixel( x, y, color,base);
		}

	}
}


void draw_circle(int x0, int y0, int radius , int color, char *base)
{
  int f = 1 - radius;
  int ddF_x = 1;
  int ddF_y = -2 * radius;
  int x = 0;
  int y = radius;

  set_pixel(x0, y0 + radius , color , base );
  set_pixel(x0, y0 - radius , color , base );
  set_pixel(x0 + radius, y0 , color , base );
  set_pixel(x0 - radius, y0 , color , base );

  while(x < y)
  {
    // ddF_x == 2 * x + 1;
    // ddF_y == -2 * y;
    // f == x*x + y*y - radius*radius + 2*x - y + 1;
    if(f >= 0)
    {
      y--;
      ddF_y += 2;
      f += ddF_y;
    }
    x++;
    ddF_x += 2;
    f += ddF_x;
    set_pixel(x0 + x, y0 + y , color , base );
    set_pixel(x0 - x, y0 + y , color , base );
    set_pixel(x0 + x, y0 - y , color , base );
    set_pixel(x0 - x, y0 - y , color , base );
    set_pixel(x0 + y, y0 + x , color , base );
    set_pixel(x0 - y, y0 + x , color , base );
    set_pixel(x0 + y, y0 - x , color , base );
    set_pixel(x0 - y, y0 - x , color , base );
  }
}

#define CHR_H 16

void drawCharAt(char c, int x, int y, int fore_color, int back_color,
				int char_scale, char* video_base, char* table)
{
	// posi��o do car�cter �c� em �table� (lida de po.dat)
	char* char_def = table + c * CHR_H;

	int i, j;
	Byte mask = 0x80;
	int color;
	int x_act, y_act, sx, sy;
	// usar operadores bitwise para isolar e testar cada um dos bits
	for (j = 0, y_act = y; j < CHR_H; j++, y_act += char_scale) // for each character row
	{
		for (i = 0, x_act = x; i < 8; i++, x_act += char_scale)		// for each pixel in row
		{
			color = (*char_def & (mask >> i)) ? fore_color : back_color;
			for(sx = 0; sx < char_scale; sx++)
					for(sy = 0; sy < char_scale; sy++)
						set_pixel(x_act + sx, y_act + sy, color, video_base);
		}
		char_def++;
	}
}	


void drawStringAt(char* str, int x, int y, int fore_color, int back_color,
				int char_scale, char *video_base, char *table)
{
	int i = 0;
	while(str[i] != 0)
	{
		drawCharAt(str[i], x + 8 * char_scale * i, y, fore_color, back_color, char_scale, video_base, table);
		i++;
	}
}
		
		
void drawIntAt(int num, int x, int y, int fore_color, int back_color,
				int char_scale, char *video_base, char *table)
{
	int i = 0;
	
	//se o numero for negativo, imprimir '-'
	if(num < 0)
	{
		drawCharAt('-', x, y, fore_color, back_color, char_scale, video_base, table);
		i++;
		num = 0 - num;
	}
	
	//descobrir a ordem de grandeza do numero
	int ordem = 1;
	while(num / ordem >= 10) ordem *= 10;
	
	//da esquerda para a direita, imprimir os algarismos
	while(true)
	{
		int digito = num / ordem;
		drawCharAt(digito + '0', x + 8 * char_scale * i, y, fore_color, back_color, char_scale, video_base, table);
		num -= digito * ordem;
		if(ordem == 1) break;
		ordem /= 10;
		i++;
	}
}

void drawTitle(char * title, int xi, int yi, int xf, int yf, int fore_color, int back_color, char * base, int char_scale, char * table)
{
	// Rectângulo de fora
	draw_line(xi, yi, xf, yi, fore_color, base);	// linha horizontal superior
	draw_line(xi, yi, xi, yf, fore_color, base);	// linha vertical esquerda
	draw_line(xi, yf, xf, yf, fore_color, base);	// linha horizontal inferior
	draw_line(xf, yi, xf, yf, fore_color, base);	// linha vertical direita

	// Rectângulo de dentro
	draw_line(xi + 150, yi + 70, xf - 150, yi + 70, fore_color, base);	// linha horizontal superior
	draw_line(xi + 150, yi + 70, xi + 150, yf - 70, fore_color, base);	// linha vertical esquerda
	draw_line(xi + 150, yf - 70, xf - 150, yf - 70, fore_color, base);	// linha horizontal inferior
	draw_line(xf - 150, yi + 70, xf - 150, yf - 70, fore_color, base);	// linha vertical direita

	// Título
	drawStringAt(title, xi + 10, yi + 10, fore_color, back_color, char_scale, base, table);
}


void drawBox( int xi, int yi, int xf, int yf, int fore_color, int fill , char * base )
{
 // Rectângulo de fora
	draw_line(xi, yi, xf, yi, fore_color, base);	// linha horizontal superior
	draw_line(xi, yi, xi, yf, fore_color, base);	// linha vertical esquerda
	draw_line(xi, yf, xf, yf, fore_color, base);	// linha horizontal inferior
	draw_line(xf, yi, xf, yf, fore_color, base);	// linha vertical direita
	if ( fill )
	{
		if ( yf < yi )
		{
			int tmp = yi;
			yi = yf;
			yf = tmp;
		}
		while ( yi < yf )
		{
				draw_line(xi, yi + 1, xf, yi + 1, fore_color, base);	// linha horizontal superior
				++yi;
		}
	}
	

}

static Sprite * title = NULL;

void drawSpriteTitle( int x , int y , char * base )
{
	if ( title == NULL )
		title = create_sprite(title_xpm, base );
	
	if ( title == NULL )
		return;
	title->x = x;
	title->y = y;
    draw_sprite(title, base);


}

